﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace LiteratureManage.Domain.DataBackup
{
    public class DefaultDBBackupProvider : IDBBackupProvider
    {
        private string _appDirPath;
        private string _backupDirPath;

        private readonly string _backupDir;
        private readonly IDBBackupFileNameGenerator _generator;
        private IList<DBTableBackup> _dbTableBackups;

        public IList<DBTableBackup> DBTableBackups { 
            get { return this._dbTableBackups; } 
            set { this._dbTableBackups = value; } }

        public DefaultDBBackupProvider(string backupDir, IDBBackupFileNameGenerator generator)
        {
            this._appDirPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            this._backupDirPath = Path.Combine(this._appDirPath, backupDir);
            this._generator = generator;
        }

        #region IDBBackupProvider Members

        public void Backup(DateTime theDate)
        {
            var dateBackupDirName = this._generator.Generate(theDate);
            var dateBackupDirPath = Path.Combine(this._backupDirPath, dateBackupDirName);
            ensureDirectoryExist(dateBackupDirPath);
            backupAllConfigurableDBTables(theDate, dateBackupDirPath);
            var gzipFileName = dateBackupDirName + ".gzip";
            gzipCompressAllBackupFiles(dateBackupDirPath, gzipFileName);
            deleteAllBackupFiles(dateBackupDirPath);
        }

        public void Restore(string dbBackupFile)
        {
            resetAllConfigurableDBTables();

            var dateBackupDir = Path.GetFileNameWithoutExtension(dbBackupFile);
            var dateBackupDirPath = Path.Combine(this._backupDirPath, dateBackupDir);
            ensureDirectoryExist(dateBackupDirPath);
            gzipDecompressAllBackupFiles(dateBackupDirPath, dbBackupFile);
            restoreAllConfigurableDBTables(dateBackupDirPath);
            deleteAllBackupFiles(dateBackupDirPath);
        }

        public void Delete(string dbBackupFile)
        {
            var dbBackupFilePath = GetDBBackupFilePath(dbBackupFile);
            File.Delete(dbBackupFilePath);
        }

        public string GetDBBackupFilePath(string dbBackupFile)
        {
            return Path.Combine(this._backupDirPath, dbBackupFile); 
        }

        public IList<DBBackupFileInfo> GetBackupFiles()
        {
            IList<DBBackupFileInfo> backupFiles = null;
            if (Directory.Exists(this._backupDirPath))
            {
                var directoryInfo = new DirectoryInfo(this._backupDirPath);
                var fileInfoArray = directoryInfo.GetFiles();
                backupFiles = (from fileInfo in fileInfoArray
                               orderby fileInfo.CreationTime descending
                               select new DBBackupFileInfo()
                                   {
                                       FileName = fileInfo.Name,
                                       FilePath = Path.Combine(this._backupDirPath, fileInfo.Name),
                                       ContentLength = (fileInfo.Length / 1024d).ToString("F2"),
                                       CreationTime = fileInfo.CreationTime
                                   }).ToList();
            }
            return backupFiles;
        }

        #endregion

        private void backupAllConfigurableDBTables(DateTime theDate, string dateBackupDirPath)
        {
            foreach (var dbTableBackup in this.DBTableBackups.OrderBy(t=>t.BackupOrder).AsEnumerable())
            {
                dbTableBackup.Backup(theDate, dateBackupDirPath);
            }
        }

        private void restoreAllConfigurableDBTables(string dateBackupDirPath)
        {
            foreach (var dbTableBackup in this.DBTableBackups.OrderBy(t => t.RestoreOrder).AsEnumerable())
            {
                dbTableBackup.Restore(dateBackupDirPath);
            }
        }

        private void resetAllConfigurableDBTables()
        {
            foreach (var dbTableBackup in this.DBTableBackups.OrderBy(t => t.ResetOrder).AsEnumerable())
            {
                dbTableBackup.Reset();
            }
        }

        private void gzipCompressAllBackupFiles(string dateBackupDirPath, string gzipFileName)
        {
            GZip.GZip.Compress(dateBackupDirPath, this._backupDirPath, gzipFileName);
        }

        private void gzipDecompressAllBackupFiles(string dateBackupDirPath, string gzipFileName)
        {
            GZip.GZip.Decompress(this._backupDirPath, dateBackupDirPath, gzipFileName);
        }

        private void deleteAllBackupFiles(string dateBackupDirPath)
        {
            Directory.Delete(dateBackupDirPath, true);
        }

        private void ensureDirectoryExist(string dir)
        {
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
        }
    }
}
